package object;

import javax.vecmath.Color3f;

import ray.Ray;
import util.Point3D;
import util.Vector3D;

public class Triangle extends SceneObject {

	private Point3D a, b, c;

	public Triangle(String name, Point3D a, Point3D b, Point3D c, Color3f color) {
		super(name, color, 0.0f, 0.0f);

		this.a = a;
		this.b = b;
		this.c = c;
	}

	@Override
	public Point3D getClosestIntersectionPoint(Ray ray) {
		// normal
		Vector3D normal = this.getNormalAtPoint(null);

		// p
		float x = a.getX();
		float y = a.getY();
		float z = a.getZ();

		// d
		float d = (normal.getX() * x) + (normal.getY() * y)
				+ (normal.getZ() * z);

		// ray1
		float xa = ray.getOrigin().getX();
		float ya = ray.getOrigin().getY();
		float za = ray.getOrigin().getZ();

		// ray2
		float xb = ray.getEnd().getX();
		float yb = ray.getEnd().getY();
		float zb = ray.getEnd().getZ();

		// calculate denominator of t
		float denominator = ((normal.getX() * (xb - xa))
				+ (normal.getY() * (yb - ya)) + (normal.getZ() * (zb - za)));

		// Return value
		Point3D intersectionPoint;
		// If the ray intersects, return where
		// If not, return null
		if (denominator != 0) {

			// t
			float t = (d - (normal.getX() * xa) - (normal.getY() * ya) - (normal
					.getZ() * za))
					/ denominator;

			// Ia + (Ib - Ia) * t
			intersectionPoint = new Point3D(xa + (xb - xa) * t, ya + (yb - ya)
					* t, za + (zb - za) * t);

			// Return true if point lies within triangle
			if (pointInsideTriangle(intersectionPoint)) {
				return intersectionPoint;
			} else {
				return null;
			}
		} else {
			return null;
		}
	}

	@Override
	public Vector3D getNormalAtPoint(Point3D p) {
		// B - A
		Vector3D AtoB = new Vector3D(a, b);

		// C - A
		Vector3D AtoC = new Vector3D(a, c);

		// normal
		return AtoB.crossProduct(AtoC);
	}

	// A -> B
	// C is test vertex
	// p is point being tested
	private boolean sameSide(Point3D p, Point3D vertexA, Point3D vertexB,
			Point3D vertexC) {

		// temp values
		float x = p.getX();
		float y = p.getY();
		float z = p.getZ();

		float xA = vertexA.getX();
		float yA = vertexA.getY();
		float zA = vertexA.getZ();

		float xB = vertexB.getX();
		float yB = vertexB.getY();
		float zB = vertexB.getZ();

		float xC = vertexC.getX();
		float yC = vertexC.getY();
		float zC = vertexC.getZ();

		// B - A
		float xBA = xB - xA;
		float yBA = yB - yA;
		float zBA = zB - zA;

		// C - A
		float xCA = xC - xA;
		float yCA = yC - yA;
		float zCA = zC - zA;

		// P - A
		float xPA = x - xA;
		float yPA = y - yA;
		float zPA = z - zA;

		// (B - A) cross (P - A)
		float crossX1 = (yBA * zPA) - (zBA * yPA);
		float crossY1 = (zBA * xPA) - (xBA * zPA);
		float crossZ1 = (xBA * yPA) - (yBA * xPA);

		// (B - A) cross (C - A)
		float crossX2 = (yBA * zCA) - (zBA * yCA);
		float crossY2 = (zBA * xCA) - (xBA * zCA);
		float crossZ2 = (xBA * yCA) - (yBA * xCA);

		// cross1 DOT PRODUCT cross2
		float dotProduct = (crossX1 * crossX2) + (crossY1 * crossY2)
				+ (crossZ1 * crossZ2);

		// Return if point is on right side of edge
		if (dotProduct >= 0) {
			return true;
		} else {
			return false;
		}
	}

	// checks if point lies within triangle formed by above vertices
	private boolean pointInsideTriangle(Point3D p) {

		// bools
		boolean ab = false;
		boolean bc = false;
		boolean ca = false;

		// Side AB
		ab = sameSide(p, a, b, c);

		// Side BC
		bc = sameSide(p, b, c, a);

		// Side CA
		ca = sameSide(p, a, c, b);

		if (ab && bc && ca) {
			return true;
		} else {
			return false;
		}
	}

}
